// -----------------------------------------------------------------------
//  <copyright file="ApiResultWrapperFilterAttribute.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/8 3:02:06</last-date>
// -----------------------------------------------------------------------

using DaprPlus.AspNetCore.Extensions;
using DaprPlus.Authorization;
using DaprPlus.Domain;

using Microsoft.AspNetCore.Mvc.Filters;


namespace DaprPlus.AspNetCore.Mvc.Filters;

/// <summary>
/// 将API返回结果进行统一封装的过滤器特性
/// </summary>
public class ApiResultWrapperFilterAttribute : ActionFilterAttribute
{
    /// <inheritdoc />
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var provider = context.HttpContext.RequestServices;
        var logger = provider.GetLogger<ApiResultWrapperFilterAttribute>();
        if (context is { Exception: not null, ExceptionHandled: false })
        {
            var ex = context.Exception;
            logger.LogError(new EventId(), ex, ex.Message);
            if (context.HttpContext.Request.IsJsonContextType() || context.HttpContext.Request.IsAjaxRequest())
            {
                if (!context.HttpContext.Response.HasStarted)
                {
                    var result = (ex is AuthorizationException ex2)
                        ? ApiResult.FromAuthorizationResult(ex2.AuthorizationResult)
                        : new ApiResult(ex.Message, ApiResultType.Error);
                    context.Result = result.ToActionResult();
                }

                context.ExceptionHandled = true;
                return;
            }
        }

        switch (context.Result)
        {
            case StatusCodeResult statusCodeResult when context.Result is not ObjectResult { Value: ApiResult }:
                {
                    var apiResult = new ApiResult(GetApiResultType(statusCodeResult.StatusCode));

                    context.Result = new ObjectResult(apiResult)
                    {
                        StatusCode = statusCodeResult.StatusCode
                    };
                    break;
                }
            case JsonResult { Value: not ApiResult } jsonResult:
                {
                    var apiResult = jsonResult.Value == null
                        ? new ApiResult(ApiResultType.Success)
                        : new ApiResult(jsonResult.Value);
                    context.Result = new ObjectResult(apiResult) { StatusCode = jsonResult.StatusCode };
                    break;
                }
            case OkObjectResult { Value: not ApiResult } okObjectResult:
                {
                    ApiResult apiResult;
                    if (okObjectResult.Value is string msg)
                    {
                        apiResult = new ApiResult(msg);
                    }
                    else
                    {
                        apiResult = new ApiResult(okObjectResult.Value!);
                    }

                    context.Result = new ObjectResult(apiResult) { StatusCode = okObjectResult.StatusCode };
                    break;
                }
            case ObjectResult { Value: not ApiResult } objectResult:
                {
                    if (objectResult.DeclaredType?.IsSimpleType() == true)
                    {
                        return;
                    }
                    var apiResult = objectResult.Value == null
                        ? new ApiResult(ApiResultType.Success)
                        : new ApiResult(objectResult.Value);
                    context.Result = new ObjectResult(apiResult) { StatusCode = objectResult.StatusCode, DeclaredType = typeof(ApiResult) };
                    break;
                }
        }

        return;

        static ApiResultType GetApiResultType(int statusCode)
        {
            return statusCode switch
            {
                200 => ApiResultType.Success,
                203 => ApiResultType.Info,
                401 => ApiResultType.UnAuth,
                403 => ApiResultType.Forbidden,
                404 => ApiResultType.NotFound,
                423 => ApiResultType.Locked,
                _ => ApiResultType.Error
            };
        }
    }
}
